﻿#define debuglog

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;



using RequestList = System.Collections.Generic.List<Cabal.Kernel.Output.RequestItem>; //List of Request
using TargetRequest = System.Collections.Generic.Dictionary<string,System.Collections.Generic.List<Cabal.Kernel.Output.RequestItem>>; //Dictionary of Target to RequestList
using RequestIPList = System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<Cabal.Kernel.Output.RequestItem>>>; //dictionary of ip to TargetRequest
using TargetMaxNum = System.Collections.Generic.Dictionary<string, int>;
using System.Net;

/*************************
 *       *Target *Target *
 *************************
 *   IP  * RList * RList *
 *************************
 *   IP  * RList * RList *
 *************************/



namespace Cabal.Kernel.Output
{
    public class BaseOutput
    {
        public BaseOutput(CabalSoul kernel)
        {
            _kernel = kernel;
            _manager = new RequestManager(kernel);
        }



        CabalSoul _kernel;
        RequestManager _manager;
        int MaxThreadNumber
        {
            get { return _manager.MaxThreadNumber; }
            set { _manager.MaxThreadNumber = value; }
        }

        public int ThreadNumber
        {
            get { return _manager.RunningMission.Count; }
        }
        public RequestManager Manager
        {
            get { return _manager; }
        }
        public List<RequestItem> Responses
        {
            get { return _manager.Responeses; }
        }



        public bool AddRequest(RequestItem item)
        {
            _manager.TrimRegisterList(item.RequestIP, item.TargetAddress.Host);

            item.Worker = this; //设置指针
            bool rtn = _manager.AddRequest(item);

            ActivateThreads();

            return rtn;
        }
        public void AddRequestEx(RequestItem item)
        {
            bool successed = false;
            while (!successed)
            {
                try
                {
                    successed = AddRequest(item);
                }
                catch (Exception err)
                {
                    if (item.IsWakeUp == true && err is ThreadInterruptedException)
                    {
                        item.IsWakeUp = false;
                        successed = false;

#if(debuglog)
                        dbglogt.AppendLine(string.Format("#{0}:{1}", Thread.CurrentThread.ManagedThreadId, "Thread Waked Up"));
#endif
                    }
                    else
                    {
                        throw err;
                    }
                }
            }
        }
        public bool TryAddRequest(RequestItem item)
        {
            _manager.TrimRegisterList(item.RequestIP, item.TargetAddress.Host);

            item.Worker = this; //设置指针
            if (_manager.TryAddRequest(item))
            {
                ActivateThreads();
                return true;
            }
            else
            {
                return false;
            }
        }
        public void ActivateThreads()
        {
            lock (_manager.RunningMission)
            {
                int request = _manager.Requests.Count;
                int idle = _manager.MaxThreadNumber - _manager.RunningMission.Count;
                if (idle > 0 && _manager.Requests.Count > 0)
                {
                    int newThread;
                    if (idle > request)
                        newThread = request; //请求少则=请求数
                    else
                        newThread = idle; //空闲少则=空闲

#if(debuglog)
                    dbglogt.AppendLine(string.Format("#{0}:{1} Now={2} New={3} Max={4} Requests={5}", Thread.CurrentThread.ManagedThreadId, "CreatingThread", _manager.RunningMission.Count, newThread, _manager.MaxThreadNumber, _manager.Requests.Count));
#endif

                    for (int i = 0; i < newThread; i++)
                        AddNewThread();
                }
            }
        }
        void AddNewThread()
        {
            Thread t = new Thread(new ThreadStart(ThreadMain));
            treg(t); //注册线程
            t.Start();
        }


        void ThreadMain()
        {

#if(debuglog)
            Thread me = Thread.CurrentThread;
            int meid = me.ManagedThreadId;
            dbglogt.AppendLine(string.Format("#{0}:{1}", meid, "ThreadCreated"));
#endif

            //这个改到AddNewThread()里面去了。。 treg(); //线程注册

            WebClient wc = null; // = new WebClient();
            while (_manager.Requests.Count > 0)
            {
                wc = new WebClient();

                RequestItem item = _manager.Requests.Dequeue();//获得任务

                //if (item == null)
                //    break;

#if(debuglog)
                dbglogt.AppendLine(string.Format("#{0}:{1}<{2}> BuffCount={3}", meid, "GotJob", item.Describe, _manager.Requests.Count));
#endif
                
                reg(item);//注册

                //sleepchange(item);

                trereg(item); //线程重注册

                sendRequest(wc, item);//执行

#if(debuglog)
                if(item.IsError)
                dbglogt.AppendLine(string.Format("#{0}:{1}<{2}>,{3},[{4}] RunningThread={5}", meid, "RequestDone!", item.Describe, item.IsError ? "Error" : "Good",item.ResponseData.Length, _manager.RunningMission.Count));
#endif

                save(item); //执行完成 存储成果

#if(debuglog)
                dbglogt.AppendLine(string.Format("#{0}:{1}<{2}> SaveBuffCount={3}", meid, "Saved", item.Describe, _manager.Responeses.Count));
#endif
                
                unreg(item);//反注册
                
                notice(item);//通知睡眠线程

                wc.Dispose();

#if(debuglog)
                dbglogt.AppendLine(string.Format("#{0}:{1}<{2}>", meid, "JobEnd", item.Describe));
#endif
            }

            tunreg(); //线程反注册

#if(debuglog)
            dbglogt.AppendLine(string.Format("#{0}:{1}", meid, "ThreadOver"));
#endif 
        }

        void reg(RequestItem item)
        {
            lock (_manager.RunningMission)
            {
                item.RunningThread = Thread.CurrentThread;
                _manager.RegisterRequest(item);
                //_manager.RunningMission.Add(item.RunningThread, item);
            }
        }
        void unreg(RequestItem item)
        {
            //_manager.RemoveRequest(item);
            _manager.TrimRegisterList(item.RequestIP, item.TargetAddress.Host);
            //_manager.RunningMission.Remove(item.RunningThread);
        }
        void treg()
        {
            lock (_manager.RunningMission)
            {
                _manager.RunningMission.Add(Thread.CurrentThread,null);
            }
        }
        void treg(Thread t)
        {
            lock (_manager.RunningMission)
            {
                _manager.RunningMission.Add(t, null);
            }
        }
        void tunreg()
        {
            lock (_manager.RunningMission)
            {
                _manager.RunningMission.Remove(Thread.CurrentThread);
            }
        }
        void trereg(RequestItem item)
        {
            lock (_manager.RunningMission)
            {
                _manager.RunningMission.Remove(item.RunningThread);
                _manager.RunningMission.Add(item.RunningThread, item);
            }
        }
        void sleepchange(RequestItem item)
        {
            lock (_manager.SleepingThread)
            {
                if (_manager.SleepingThread.ContainsKey(item.RunningThread))
                {
                    _manager.SleepingThread.Remove(item.RunningThread);
                    _manager.SleepingThread.Add(item.RunningThread, item);
                }
            }
        }
        void sendRequest(WebClient wc, RequestItem item)
        {
            try
            {
                item.RequestTime = DateTime.Now;
                switch (item.Method.ToUpper())
                {
                    case "GET":
                        sendGet(wc, item);
                        break;
                    case "POST":
                        sendPost(wc, item);
                        break;
                    case "UPLOAD":
                        sendUpload(wc, item);
                        break;
                }
                OnRequestDone(new DoneEventArgs(item));
            }
            catch (Exception err)
            {
                item.IsError = true;
                item.Error = err;

            }
            finally
            {
                item.ResopnseTime = DateTime.Now;
                item.IsDone = true;
            }
            //Thread.Sleep(1000);
        }
        void sendGet(WebClient wc, RequestItem item)
        {
            wc.Headers = item.Headers;
            item.ResponseData = wc.DownloadData(item.TargetAddress);
            item.ResponseHeaders = wc.ResponseHeaders;
        }
        void sendPost(WebClient wc, RequestItem item)
        {
            wc.Headers = item.Headers;
            item.ResponseData = wc.UploadData(item.TargetAddress, "POST", item.RequestData);
            item.ResponseHeaders = wc.ResponseHeaders;
        }
        void sendUpload(WebClient wc, RequestItem item)
        {
            wc.Headers = item.Headers;
            item.ResponseData = wc.UploadFile(item.TargetAddress, "POST", item.UploadFileNmae);
            item.ResponseHeaders = wc.ResponseHeaders;
        }
        void notice(RequestItem item)
        {
            Monitor.Enter(item.RequestThread);
            Monitor.Enter(_manager.SleepingThread);
            if (item.RequestThread.ThreadState == ThreadState.WaitSleepJoin)
            {
                //先通知请求线程
#if(debuglog)
                dbglogt.AppendLine(string.Format("#{0}:{1}<#{2},{4}>[{3}]", Thread.CurrentThread.ManagedThreadId, "Noticing",item.RequestThread.ManagedThreadId,"CallingThread",item.Describe));
#endif
                lock (_manager.SleepingThread)
                {
                    //重置SLEEP ITEM部分
                    //if (_manager.SleepingThread.ContainsKey(item.RunningThread))
                    //{
                    //    _manager.SleepingThread.Remove(item.RunningThread);
                    //    _manager.SleepingThread.Add(item.RunningThread, item);
                    //}

                    //_manager.SleepingThread[item.RequestThread].IsWakeUp = true;
                    noticeThread(item.RequestThread);
                }

                Monitor.Exit(item.RequestThread);
                Monitor.Exit(_manager.SleepingThread);
            }
            else
            {
#if(debuglog)
                dbglogt.AppendLine(string.Format("#{0}:{1}<#{2},{5}>[{3}] SleepCount={4}", Thread.CurrentThread.ManagedThreadId, "Noticing", item.RequestThread.ManagedThreadId, "OhterThread", _manager.SleepingThread.Count,item.Describe));
#endif
                Monitor.Exit(item.RequestThread);
                if (_manager.SleepingThread.Count > 0)
                {

                    //若不行随机通知一个线程
                    int index = _kernel.RandomGener.Next(_manager.SleepingThread.Count);
                    int i = 0;
                    Thread target = null;
                    lock (_manager.SleepingThread)
                    {
                        foreach (Thread t in _manager.SleepingThread.Keys)
                        {
                            if (i == index)
                            {
                                target = t;
                                break;
                            }
                            i++;
                        }
                    }

                   //item.IsWakeUp = true;
                   // _manager.SleepingThread[target].IsWakeUp = true;
                   noticeThread(target);
                }
                Monitor.Exit(_manager.SleepingThread);
            }
    
        }
        void save(RequestItem item)
        {
            _manager.SaveResponse(item);

            if (item.IsError)
                OnRequestException(new DoneEventArgs(item));
            else
                OnRequestDone(new DoneEventArgs(item));
        }

        void noticeThread(Thread thread)
        {
            if (_manager.SleepingThread.ContainsKey(thread))
            {
                RequestItem item = _manager.SleepingThread[thread];
                item.IsWakeUp = true;

                if (thread.ThreadState == ThreadState.WaitSleepJoin && item.IsWakeUp)
                {
#if(debuglog)
                    dbglogt.AppendLine(string.Format("#{0}:{1}<#{2},{5}> {3} SleepCount={4}", Thread.CurrentThread.ManagedThreadId, "Noticed", thread.ManagedThreadId, "Done!", _manager.SleepingThread.Count,item.Describe));
#endif
                    item.WakeUpRequestThread();
                }
#if(debuglog)
                else
                {
                    dbglogt.AppendLine(string.Format("#{0}:{1}<#{2},{7}> {3}  SleepCount={4} TState={5},IsWakeUp={6}", Thread.CurrentThread.ManagedThreadId, "Noticed", item.RequestThread.ManagedThreadId, "Failed!", _manager.SleepingThread.Count, thread.ThreadState, item.IsWakeUp,item.Describe));
                }
#endif
                _manager.SleepingThread.Remove(thread);
            }
        }
        void noticeThread(Thread thread,RequestItem item)
        {
            if (_manager.SleepingThread.ContainsKey(thread))
            {
                //RequestItem item = _manager.SleepingThread[thread];
                if (thread.ThreadState == ThreadState.WaitSleepJoin && item.IsWakeUp)
                {
#if(debuglog)
                    dbglogt.AppendLine(string.Format("#{0}:{1}<#{2},{5}> {3} SleepCount={4}", Thread.CurrentThread.ManagedThreadId, "Noticed", thread.ManagedThreadId, "Done!", _manager.SleepingThread.Count, item.Describe));
#endif
                    item.WakeUpRequestThread();
                }
#if(debuglog)
                else
                {
                    dbglogt.AppendLine(string.Format("#{0}:{1}<#{2},{7}> {3}  SleepCount={4} TState={5},IsWakeUp={6}", Thread.CurrentThread.ManagedThreadId, "Noticed", item.RequestThread.ManagedThreadId, "Failed!", _manager.SleepingThread.Count, thread.ThreadState, item.IsWakeUp, item.Describe));
                }
#endif
                _manager.SleepingThread.Remove(thread);
            }
        }



        protected virtual void OnRequestDone(DoneEventArgs e)
        {
            if (RequestOver != null)
                RequestOver(this, e);
            if(RequestDone != null)
                RequestDone(this, e);
        }
        protected virtual void OnRequestException(DoneEventArgs e)
        {
            if (RequestOver != null)
                RequestOver(this, e);
            if (RequestException != null)
                RequestException(this, e);
        }

        public event OutputEventHandler RequestOver;
        public event OutputEventHandler RequestDone;
        public event OutputEventHandler RequestException;
    }

    
}
